//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#ifndef __FAT_H_
#define __FAT_H_
/*
 * fat.h
 *    Definitions for FAT-type filesystem
 *
 * Throughout this filesystem server no attempt has been made to
 * insulate from the endianness of the processor.  It is assumed that
 * if you are running with FAT disks you're an Intel-type processor.
 * If you're on something else, you probably will make a FAT-ish
 * filesystem for yourself, with your own byte order, and enjoy the
 * savings in CPU time.  The exception is floppies; for those,
 * perhaps the mtools would suffice.  Or just use tar.
 */

//#include <time.h>
#include <misc.h>
#include <tfssys.h>

#define   FAT32       32
#define   FAT16       16
#define   FAT12       12
#define   MAX_FILE_NAME   (256)     // 255 characters and a '\0'
#define   FULL_PATH_LEN   (260)     // The full path length contain a '\0'

/*
 * This represents the cluster allocation for a directory or file.
 * The c_clust field points to an array of UInt32's which
 * are the clusters allocated.  It is malloc()'ed, and realloc()'ed
 * as needed to change storage allocation.
 */
struct clust
{
    UInt32 *c_clust;       /* Clusters allocated */
    UInt32 c_nclust;       /* # entries in c_clust */
    UInt32 n_newclust;  /* points to an element of 'c_clust', for tag the first */
    /* allocated cluster when the file be appended, */
    /* initialize value by 0x7fffffff*/
};

/*
 * An open FAT file/dir has one of these.  It is thrown away on last close
 * of a file; it is kepts indefinitely for directories, unless the
 * directory itself is deleted.  In the future, it might be worth keeping
 * it for a while to see if it'll be needed again.
 */
struct node
{
    IMutex *n_lock;          /* lock for safe */
    UInt32 n_type;            /* Type field */
    struct clust *n_clust;  /* Block allocation for this file */
    UInt32 n_mode;            /* Protection bits */
    UInt32 n_opmode;          /* now operation mode */
    UInt32 n_sharemode;    /* share mode (DENY, READ, WRITE)*/
    UInt32 n_firsthandle;     /* a handle which first open this node*/
    UInt32 n_refs;            /* Current open files on us */
    UInt32 n_flags;           /* Flags */
    struct node *n_dir;     /* Dir we exist within */
    UInt32 n_slot;            /*  ...index within */
    UInt32 n_inum;           /* Inode #, for stat/FS_FID */

    /* For T_FILE only */
    UInt32 n_len;            /* Our byte length */

    /* For T_DIR only */
    struct hash *n_files;   /* Hash for index->file mapping */
};

/*
 * Values for n_type
 */
#define T_DIR 1        /* Directory */
#define T_FILE 2       /* File */
#define T_SYM 3        /* Symlink */

/*
 * Bits for n_flags
 */
#define N_DIRTY 1      /* Contents modified */
#define N_DEL 2        /* Node has been removed */
#define N_FID 4        /* Node has had an FS_FID done, may be cached */

/*
 * A FAT directory entry
 */
struct directory
{
    UInt8   DIR_Name[ 8 ];     /*  0        7   file name and file extension */
    UInt8   DIR_Ext[ 3 ];      /*  8        10  file extension */
    UInt8   DIR_Attr;          /*  11       1   attribute UInt32 */

    UInt8   DIR_NTRes;         /*  12       1   Case for base and extension */
    UInt8   DIR_CrtTimeTenth;  /*  13       1   Creation time, milliseconds */
    UInt16  DIR_CrtTime;       /*  14       2   Creation time */
    UInt16  DIR_CrtDate;       /*  16       2   Creation date */
    UInt16  DIR_LstAccDate;    /*  18       2   Last access date */
    UInt16  DIR_FstClusHI;     /*  20    2   High 16 bits of cluster in FAT32 */

    UInt16  DIR_WrtTime;     /*    22       2   time stamp */
    UInt16  DIR_WrtDate;     /*    24       2   date stamp */
    UInt16  DIR_Start;       /*    26       2   first cluster */
    UInt32  DIR_FileSize;    /*    28       4   file size (in bytes) */
};

/*
 * Bits in "attr"
 */
#define DA_READONLY  0x01    /* Read only */
#define DA_HIDDEN   0x02    /* Hidden */
#define DA_SYSTEM   0x04    /* System */
#define DA_VOLUME   0x08    /* Volume label */
#define DA_DIR      0x10    /* Subdirectory */
#define DA_ARCHIVE  0x20    /* Needs archiving */
#define DA_VFAT     (0x0f)  /* Illegal combo == vFAT */

const UInt32 ATTR_LONG_NAME = DA_READONLY | DA_HIDDEN | DA_SYSTEM | DA_VOLUME;
const UInt32 ATTR_LONG_NAME_MASK = ATTR_LONG_NAME | DA_DIR | DA_ARCHIVE;

/*
 * Special char for name, indicating deleted
 */
#define DN_DEL (0xE5)

/*
 * Bits in "DIR_NTRes"
 */
#define RES_LOWCASE 0x08 /* all-lowercase 8.3 filename flag */

/*
 * The start/startHi fields need to be handled differently
 * for FAT-32 as opposed to earlier filesystems.
 */
#define START(d) \
    ((p_Sharedata->fat_size == FAT32) ? \
    (((d)->DIR_FstClusHI << 16) | (d)->DIR_Start) : \
    (d)->DIR_Start)

#define SETSTART(d, v) \
    if (p_Sharedata->fat_size == FAT32) { \
    (d)->DIR_FstClusHI = ((v) >> 16); \
    (d)->DIR_Start = (v);\
    }else{ \
    (d)->DIR_Start = (v); \
}

/*
 * VFAT sub-entry
 */
#define VSE_1SIZE (5)
#define VSE_2SIZE (6)
#define VSE_3SIZE (2)
#define VSE_NAME_SIZE (VSE_1SIZE + VSE_2SIZE + VSE_3SIZE)
struct dirVSE
{
    UInt8 dv_id;                   /* See flags below sequence number for slot*/
    UInt8 dv_name1[VSE_1SIZE*2];   /* first 5 characters in name */
    UInt8 dv_attr;                 /* Fixed value, below */
    UInt8 dv_hash;                 /* Always 0 */
    UInt8 dv_sum;                  /* Checksum of short name */
    UInt8 dv_name2[VSE_2SIZE*2];   /* 6 more characters in name */
    UInt16 dv_sector;              /* starting cluster number, Always 0 */
    UInt8 dv_name3[VSE_3SIZE*2];   /* last 2 characters in name */
};

#define VSE_ID_LAST (0x40)         /* Last VSE segment */
#define VSE_ID_MASK (0x1F)         /*  ...low bits are ID index */
#define VSE_ID_MAX (20)            /* 20 VSE's permits 256 char name */
#define VSE_ATTR_VFAT DA_VFAT      /* Map struct directory -> dirVSE */
#define VSE_MAX_NAME ((VSE_NAME_SIZE) * (VSE_ID_MAX))  /*VSE MAX File length*/

#if defined(_x86) || defined(_EVC)
#pragma pack(push, 1)
#endif // _x86

/*
 * Shape of bytes at offset 36 in boot block for FAT 12/16
 * (unused, included here for completeness only)
 */
struct DECL_PACKED boot16 {
    UInt32 BS_DrvNum:8;              /*    36        1 */
    UInt32 BS_Reserved1:8;           /*    37        1 */
    UInt32 BS_BootSig:8;             /*    38        1 */
    UInt32 BS_VolID:32;              /*    39        4 */
    UInt8  BS_VolLab[ 11 ];          /*    43        11 */
    UInt8  BS_FilSysType[ 8 ];       /*    54 */
    UInt8  BS_res_2m;                /*    62 reserved by 2M */
    UInt8  BS_CheckSum;              /*    63 2M checksum (not used) */
    UInt8  BS_fmt_2mf;               /*    64 2MF format version */
    UInt8  BS_wt;                    /*    65 1 if write track after format */
    UInt8  BS_rate_0;                /*    66 data transfer rate on track 0 */
    UInt8  BS_rate_any;              /*    67 data transfer rate on track<>0 */
    UInt16 BS_BootP;                 /*    68 offset to boot program */
    UInt16 BS_Infp0;                 /*    70 T1: information for track 0 */
    UInt16 BS_InfpX;                 /*    72 T2: information for track<>0 */
    UInt16 BS_InfTm;                 /*    74 T3: track sectors size table */
    UInt16 BS_DateF;                 /*    76 Format date */
    UInt16 BS_TimeF;                 /*    78 Format time */

};

/*
 * Shape of bytes at offset 36 in boot block for FAT-32
 */
struct DECL_PACKED boot32 {
    UInt32  BPB_FATSz32;         /* 36    4 */
    UInt16  BPB_ExtFlags;        /* 40    2    bit 8: fat mirroring, */
                                 /*            low 4: active fat */
    UInt16  BPB_FSVer;           /* 42    2   major, minor filesystem version */
    UInt32  BPB_RootClus;        /* 44    4   first cluster in root directory */
    UInt16  BPB_FSInfo;          /* 48    2    filesystem info sector */
    UInt16  BPB_BkBootSec;       /* 50    2    backup boot sector */
    UInt8   BPB_Reserved[ 12 ];  /* 52    12 */
    UInt8   BS_DrvNum;           /* 64    1 */
    UInt8   BS_Reserved1;        /* 65    1 */
    UInt8   BS_BootSig;          /* 66    1 */
    UInt32  BS_VolID;            /* 67    4 */
    UInt8   BS_VolLab[ 11 ];     /* 71    11 */
    UInt8   BS_FilSysType[ 8 ];  /* 82 */

};

/*
 * Format of sector 0 in filesystem
 */
struct DECL_PACKED boot {
    UInt8   BS_jmpBoot[ 3 ];    /*  0    3  Jump instruction to boot code */
    UInt8   BS_OEMName[ 8 ];    /*  3    8  indication of what ATTR_DIRECTORY */
                                /*          formatted the volume */
    UInt8   BPB_BytsPerSec0;    /*  11   2  Count of bytes per sector */
    UInt8   BPB_BytsPerSec1;
    UInt8   BPB_SecPerClus;     /* 13 1  Number of sectors per allocation unit*/
    UInt16  BPB_ResvdSecCnt;    /*  14    2  Number of reserved sectors in the*/
                                /*        Reserved region of the volume starting                                        at the first sector of the volume */
    UInt8   BPB_NumFATs;        /*  16    1    number of FATs */
    UInt8   BPB_RootEntCnt0;    /*  17    2    root directory entries */
    UInt8   BPB_RootEntCnt1;
    UInt8   BPB_TotSec16_0;     /*  19    2    number of sectors */
    UInt8   BPB_TotSec16_1;
    UInt8   BPB_Media;          /*  21    1    media code (unused) */
    UInt16  BPB_FATSz16;        /*  22    2    sectors/FAT */
    UInt16  BPB_SecPerTrk;      /*  24    2    sectors per track */
    UInt16  BPB_NumHeads;       /*  26    2    number of heads */
    UInt32  BPB_HiddSec;        /*  28    4    hidden sectors (unused) */
    UInt32  BPB_TotSec32;       /*  32    number of sectors (if sectors == 0) */
    union {
        struct boot32 boot32;
        struct boot16 boot16;
    } boottype;
};

#if defined(_x86) || defined(_EVC)
#pragma pack(pop)
#endif // _x86

typedef struct FileStat {
    UInt32 mode;
    UInt32 atime;
    UInt32 mtime;
    UInt32 ctime;
    UInt32 size;
    UInt64 blocks;
    UInt32 blksize;
} FILESTAT;

/*
 * Parameters for block cache
 */
#define CLSIZE (p_Sharedata->bootb.BPB_SecPerClus)
#define BLOCKSIZE (p_Sharedata->clsize)

/*
 * Mapping between cluster numbers and underlying byte offsets
 */
#define BOFF(clnum) (((clnum) - 2) * CLSIZE + p_Sharedata->data0)

EXTERN_C Void pack_name(struct directory *d, wchar_t *buf);
EXTERN_C UInt8 short_checksum(char *f1, char *f2);

class CChangeNotifier;

class fatfs_sharedata
{
public:
    fatfs_sharedata();
    ~fatfs_sharedata();
    UInt32  AddRef(void);
    UInt32  Release(void);

    struct boot bootb;
    struct node *rootdir;
    struct node *procroot;
    struct hash *dirhash;
    UInt32 clsize;
    UInt32 dirents;
    UInt32 root_cluster;
    int fat_dirty;        /* A change has occurred in the FAT */
    UInt32 fat_size;        /* FAT format (12, 16, or 32) */
    UInt32 nclust;          /* # clusters in filesystem */
    UInt32 data0;
    UInt32 secsz;
    UInt32 secshift;
    PDevDriver pBlkDev;
    CChangeNotifier *pNotifier;
    int m_sdCardEjected;
    UInt32 bpb_base;
    IEvent* pSyncBufEvent;
    Boolean bExit;
private:
    UInt32 m_nRef;
};

#include "fat12.h"
#include "fat16.h"
#include "fat32.h"

class fat
{
public:
    int fat_loadfat(class fatfs_sharedata *);
    int fat_init(class fatfs_sharedata *);
    UInt32 fat_checkmbr(char *pBuf);
    int clust_setlen(struct clust *c, UInt32 newclust);
    struct clust * alloc_clust(struct directory *d);
    void free_clust(struct clust *c);
    void fat_sync(void);
    UInt32 get_clust(struct clust *c, UInt32 idx);
    UInt64 fat_calcfreespace();
    fat();
    ~fat();
    UInt32 AddRef();
    UInt32 Release();

#if defined(_DEBUG)
    void fat_stat(UInt32 *Fat_secs);
#endif

    class fatfs_sharedata *p_Sharedata;

private :
    inline void FatLock();
    inline void FatUnlock();
    class fat12 *pFat12 ;
    class fat16 *pFat16 ;
    class fat32 *pFat32 ;
    IMutex *fat_lock;
    UInt32 m_nRef;
};

class FatDir
{
public:
    int  dir_init(void *, void *, class fatfs_sharedata *);
    int  dir_loadhash(UInt32);
    struct node * dir_look(struct node *, wchar_t *);
    int  dir_empty(struct node *);
    void dir_remove(struct node *);
    struct node * dir_newfile(class CFatFile *, wchar_t *, int);
    int  dir_copy(struct node *, UInt32, struct directory *);
    void timestamp(struct directory *, time_t);
    void dir_timestamp(struct node *, time_t);
    void dir_timeaccess(struct node *, time_t);
    int  dir_setlen(struct node *);
    int  dir_checkcontent(struct directory *);
    int  dir_checkentry(struct directory *pd);
    void root_sync(void);

    int  fatfs_rename(class CFatFile *, wchar_t *,
                     class CFatFile *, wchar_t *);
    void cancel_rename(class CFatFile *f);
    int  dir_set_type(class CFatFile *f, char *newtype);
    void dir_readonly(class CFatFile *f, int readonly);
    void ref_node(struct node *n);
    int deref_node( struct node *n);
    void sync(void);
    void Syncn(struct node *pNode);
    int  fatfs_open(class CFatFile *f, wchar_t *filename,
                   int mode, int mask);
    void fatfs_close(class CFatFile *f);
    int  fatfs_remove(class CFatFile *f, wchar_t *filename);
    int  fatfs_remove(class CFatFile *f, struct node *pNode);
    int  fatfs_write(class CFatFile *f, unsigned char *buf,
                    UInt32 size, UInt32 *Written);
    int  fatfs_read( class CFatFile *f, unsigned char *buf,
                    UInt32 Size, UInt32 *pSizeRead);
    void fatfs_GetSize(class CFatFile *f, UInt64 *pSize);
    int  fatfs_fid(class CFatFile *f, UInt32 *iNUm, UInt32 *iSize);
    int  fatfs_SetSize(class CFatFile *f, UInt32 Size);
    void fatfs_stat(class CFatFile *f, FILESTAT *buf, UInt32 *pSizeRead);
    int fatfs_wstat(class CFatFile *f, FILESTAT *buf, UInt32 pSizeRead);
    int fatfs_seek( class CFatFile *f, UInt32 pos);
    void fatfs_lock(void);
    void fatfs_unlock(void);
    int  fatfs_islock(void);
    void dir_treatFileName(wchar_t *fileName);
#if defined(_DEBUG)
    void FatDir_stat(UInt32 *Fat_secs, UInt32 *Data_secs, UInt32 *Cache_secs);
#endif

    FatDir():rootsize(0), root_dirty(0), cldirs(0), clshift(0), m_nRef(0)
    {
        FatLock = 0;
        pDiskAbc = NULL;
        pFat = NULL;
        rootdirents = NULL;
        rename_pending = NULL;
        p_Sharedata = NULL;
    }
    ~FatDir();

    UInt32 AddRef();
    UInt32 Release();

    class fatfs_sharedata *p_Sharedata;

private:
    void ddirty( void *handle);
    void dfree( void *handle);
    int search_vfat(struct directory *, int, struct directory *, wchar_t *);
    int search_dir(int, struct directory *, struct directory *, char *, char *, wchar_t *);
    int root_search(char *f1, char *f2, struct directory *dp, wchar_t *);
    int dir_search( struct node *n, char *f1, char *f2, struct directory *dp, wchar_t *);
    int map_type(int fat_attr);
    UInt32 get_inum(struct node *dir, int idx);
    int node_search(struct node *, char *, char *, struct directory *, wchar_t *);
    struct directory * get_dirent(struct node *n, UInt32 idx, void **handlep);
    int dir_findslot(struct node *n, int nentry);
    int unicode_set_chars(UInt8 *ubuf, char *buf, UInt32 len);
    void unique_filename(char *file, char *f1, char *f2, struct node *n, wchar_t *filename);
    int tree_contains(struct node *tree, struct node *n);
    void fix_dotdot(struct node *n, struct node *ndir);
    int check_busy(struct node *n);
    int do_rename(class CFatFile *, wchar_t *, class CFatFile *, wchar_t *);
    void move_file(class CFatFile *f, struct node *n);
    int do_write(struct clust *c, UInt32 pos, char *buf, UInt32 cnt);
    int write_zero(struct node *n, UInt32 oldlen, UInt32 newlen);
    int fatfs_readdir(class CFatFile *, unsigned char *, UInt32, UInt32 *);
    int assemble_vfat_name(wchar_t *, struct directory *, intAssemblefun , void *);
    UInt32 isize(struct node *n);
    UInt32 fatfs_acc(struct directory *d);
    struct node *
    dir_getNodeByDirEntry(struct node *pParentNode, struct directory &d, int nSlot);

    void time_init(struct directory *d);
    void time_access(struct directory *, time_t);

    class abc *pDiskAbc;
    class fat *pFat;

    int     FatLock;            /*The Block Device is Used by other, So lock Partition*/
    UInt32    rootsize;           /* # bytes in root dir data */
    int     root_dirty;         /*  ...needs to be flushed to disk */
    struct  directory *rootdirents; /* Root dir contents */
    UInt32    cldirs,             /* # dir entries in a cluster */
            clshift;            /*  ...# bits to shift this value */
    struct hash  *rename_pending;   /* Tabulate pending renames */
    UInt32 m_nRef;
};

#endif // __FAT_H_
